package Manager;
import Commons.Indirizzi;
import Commons.Logging.LogStatoCoda;
import Commons.Logging.Logger;
import Commons.Logging.LoggerContenutoCode;
import Commons.Utility;
import Commons.Workflow;
import java.util.ArrayList;
import java.util.HashMap;
import javax.xml.stream.XMLStreamException;


/**Questa classe descrive il WFQueueManager.
 *
 * @author Team5
 */
public class WFQueueManager implements WFQueue, Runnable {

    /**Coda dei Workflow
     *
     */
    private ArrayList<Workflow> wfQueue;

    

    /**
     * Oggetto per la gestione del logging dello stato della coda
     */
    private Logger l;

    /**
     * Oggetto per la gestione del logging del contenuto della coda
     */
    private LoggerContenutoCode lcq;

    /**
     * ID della coda
     */
    private String id;
    /**
     * Costruttore di default
     */

    /**
     * Numero totale degli elementi inseriti in coda
     */
    private int NumeroTotaleElementiInseriti;
    private boolean condizione=true;

    public WFQueueManager() throws XMLStreamException {
        this.wfQueue=new ArrayList<Workflow>();
        this.id=this.getClass().getSimpleName()+this.hashCode();
        this.l=new Logger("log"+id+".xml",this.getClass().getSimpleName());
        this.lcq=new LoggerContenutoCode("logContenutoCode"+id+".xml");
        this.NumeroTotaleElementiInseriti=0;
    }

    WFQueueManager(String idWFQ, ArrayList<Workflow> wfQueue, HashMap<Integer, Integer> vectorClockWFQM, int posizioneVectorClockWFQM, int numeroTotaleElementiInseritiWFQM) throws XMLStreamException {
        this.wfQueue=wfQueue;
        this.id=idWFQ;
        this.l=new Logger("log"+id+".xml",this.getClass().getSimpleName(),vectorClockWFQM, posizioneVectorClockWFQM);
        this.lcq=new LoggerContenutoCode("logContenutoCode"+id+".xml");
        this.NumeroTotaleElementiInseriti=numeroTotaleElementiInseritiWFQM;
    }

    /**
     * Il metodo ritorna la coda di Workflow
     * @return coda di Workflow
     */
    public ArrayList<Workflow> getWfQueue(){
        return this.wfQueue;
    }

    /**Il metodo setta la coda di Workflow, passata come parametro in ingresso.
     *
     * @param val ArrayList di Workflow
     */
    public void setWfQueue(ArrayList<Workflow> val){
        this.wfQueue=val;
    }



    /** Metodo ridefinito dall'interfaccia WFQueue
     *
     * @param wf Rappresenta il Workflow da inserire nella coda
     * @return
     */
    public int  enqueue(Workflow wf) throws InterruptedException {
        synchronized(wfQueue){
        this.wfQueue.add(wf);
        l.merge(wf.getVectorClock());
        this.NumeroTotaleElementiInseriti++;
        l.logging(new LogStatoCoda(this.getClass().getSimpleName(),id, "Aggiunto in coda "+wf.toString(),String.valueOf(System.currentTimeMillis()),this.NumeroTotaleElementiInseriti,this.wfQueue.size() ));
        lcq.logging(wfQueue);

        wfQueue.notify();
        return this.wfQueue.size();
        }
    }

     /** Metodo ridefinito dall'interfaccia WFQueue
     *
     * @return
     */
    public  Workflow dequeue() throws InterruptedException {
        //VERIFICARE SE IL SYNCHRONIZED è NECESSARIO O NO!!!!!!
         synchronized(wfQueue){
         Workflow wf = wfQueue.remove(0);
         this.NumeroTotaleElementiInseriti--;
         l.logging(new LogStatoCoda(this.getClass().getSimpleName(),id, "Rimosso dalla coda "+wf.toString(),String.valueOf(System.currentTimeMillis()),this.NumeroTotaleElementiInseriti,this.wfQueue.size() ));
         lcq.logging(wfQueue);
         wfQueue.notify();
         wf.setVectorClock(l.getVectorClock());
         return wf;
        }
    }
    public void setCondizioneFalse(){
        this.condizione=false;
    }

    

    /**Thread esecuzione Server WFQueueManager
     *
     */
    public void run() {

        Utility u=new Utility();
        Utility u2=new Utility();
        try {
            System.out.println("WFQM: inizializzo Server per ricezione wf");
            u.inizializzaServerRicezioneUDP(Indirizzi.portaInvioWF);
            Workflow wf;
            Integer a;
            //riceve il TaskDescriptor
            while(condizione){
                System.out.println("WFQM: in attesa...");
                wf=(Workflow) u.riceviOggettoSerializzatoUDP();
                System.out.println("WFQM: Ho ricevuto il wf: "+wf.getID()+"; lo inserisco nella coda");
                //genera l'Address del Worker che servirà quel TaskDescriptor
                int size=this.enqueue(wf);
                System.out.println("WFQM: aggiunto wf "+wf.getID()+" alla posizione "+size+"; elementi in coda: "+this.wfQueue.toString());
                //invia l'Address
                Thread.sleep(100);
                //debug//System.out.println("u.getMittente(): "+u.getMittente().toString());
                u2.invioOggettoSerializzatoUDP(size,u.getMittente(),Indirizzi.portaRicezioneWF);
            }
            System.out.println("WFQUEUEMANAGER_________________CLOSE");

            } catch (InterruptedException ex) {
                System.out.println("Interruzione Sicura WFQueueManager");
            
            } catch (Exception ex) {
                ex.printStackTrace();
            }
   
         finally
         {
            u.chiudiSocket();
            u2.chiudiSocket();
         }


    }

    public String getID() {
        return this.id;
    }

    public HashMap<Integer, Integer> getVCLogger() {
        return this.l.getVectorClock();
    }

    public int getNumeroElementiInseriti() {
        return this.NumeroTotaleElementiInseriti;
    }
    public Logger getL() {
        return l;
    }

    public void setL(Logger l) {
        this.l = l;
    }

}
